import json
import logging
import hashlib
from datetime import timedelta

from django.db import transaction
from django.shortcuts import render
from django.utils import timezone
from django.utils.timezone import now
from django_redis import get_redis_connection
from rest_framework.permissions import IsAuthenticated
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView

from courses.models import Course, CourseExpire
from orders.models import Order, OrderDetail
from orders.serializers import OrderDetailInfoSerializer
from utils.snowflake_singleton import SnowflakeSingleton
from orders.tasks import cancel_if_unpaid

# Create your views here.
logger = logging.getLogger('django')


class OrderCreateAPIView(APIView):
    """
    创建订单接口:

    """
    permission_classes = [IsAuthenticated]

    def post(self, request):
        # 获取前端传递的参数
        course_list = request.data.get('courseList', [])
        pay_method = request.data.get('pay_method')
        use_coupon = request.data.get('use_coupon')
        use_coupon_id = request.data.get('use_coupon_id')
        total_price = request.data.get('total_price')
        total_price = float(total_price)
        real_price = request.data.get('real_price')
        logger.info(
            f"前端传递的参数course_list:{course_list},pay_method:{pay_method},use_coupon:{use_coupon},use_coupon_id:{use_coupon_id},total_price:{total_price},real_price:{real_price}")

        if not course_list:
            logger.error("课程列表不能为空")
            return Response({
                "code": 0,
                "message": "课程列表不能为空",
            })

        user_id = request.user.id
        course_ids = [item['course_id'] for item in course_list]
        logger.info(f"用户{user_id}的购物车课程列表id:{course_ids}")

        redis_conn = get_redis_connection('cart')
        cart_key = f"code_env_spaces:carts:cartList:{user_id}"

        # 幂等键命中检查（优先返回首单结果）
        header_idemp = request.headers.get('Idempotency-Key') or request.META.get('HTTP_IDEMPOTENCY_KEY')
        if not header_idemp:
            try:
                normalized_courses = sorted([
                    {
                        'course_id': int(item.get('course_id')),
                        'expire_time': int(item.get('expire_time', 0)),
                        'expire_text': str(item.get('expire_text', '')),
                        'price': float(item.get('price', 0)),
                    }
                    for item in course_list
                ], key=lambda x: x['course_id'])
                fingerprint_payload = {
                    'user_id': user_id,
                    'courses': normalized_courses,
                    'total_price': float(total_price),
                    'real_price': float(real_price),
                    'pay_method': int(pay_method) if pay_method is not None else None,
                    'use_coupon': bool(use_coupon),
                    'use_coupon_id': int(use_coupon_id) if use_coupon_id is not None else None,
                }
                header_idemp = hashlib.md5(
                    json.dumps(fingerprint_payload, sort_keys=True, ensure_ascii=False).encode('utf-8')
                ).hexdigest()
            except Exception:
                header_idemp = None

        idemp_key = f"code_env_spaces:orders:idemp:{user_id}:{header_idemp}" if header_idemp else None
        if idemp_key:
            cached_result = redis_conn.get(idemp_key)
            if cached_result:
                try:
                    result = json.loads(cached_result.decode())
                    logger.info(f"命中幂等键，直接返回首单结果，key={idemp_key}")
                    return Response(result)
                except Exception:
                    pass

        # cart_data = redis_conn.hgetall(cart_key)

        # 检验用户传过来的课程列表 是否在购物车里面
        for course_id in course_ids:
            # key:课程ID
            course_key = f"course_{course_id}"
            cart_item = redis_conn.hget(cart_key, course_key)
            if not cart_item:
                logger.error(f"用户{user_id}的购物车课程列表id:{course_ids} 中不存在课程{course_id}")
                return Response({
                    "code": 0,
                    "message": "课程不存在",
                })

            cart_item = json.loads(cart_item.decode())
            logger.info(f"用户{user_id}的购物车课程列表id:{course_ids} 中课程{course_id}信息:{cart_item}")
            if cart_item['is_selected'] == 0:
                logger.error(f"用户{user_id}的购物车课程列表id:{course_ids} 中课程{course_id}未选中")
                return Response({
                    "code": 0,
                    "message": "课程未选中",
                })
        courses = Course.objects.filter(id__in=course_ids, status=1)
        logger.info(f"用户{user_id}的购物车课程列表id:{course_ids} 中课程信息:{courses}")

        if len(courses) != len(course_ids):
            logger.error(f"用户{user_id}的购物车课程列表id:{course_ids} 中课程不存在")
            return Response({
                "code": 0,
                "message": "存在未上架的课程",
            })

        # 自动复用未支付订单：如发现用户存在包含任一目标课程的未支付订单，直接返回该订单信息
        try:
            unpaid_orders = (
                Order.objects
                .filter(user_id=user_id, order_status=0)
                .prefetch_related('order_details')
                .order_by('-order_time', '-id')
            )
            target_course_id_set = set(course_ids)
            reuse_order = None
            for o in unpaid_orders:
                od_course_ids = {od.course_id for od in o.order_details.all()}
                if od_course_ids & target_course_id_set:
                    reuse_order = o
                    break
            if reuse_order:
                logger.info(f"复用未支付订单，order_number={reuse_order.order_number}, user_id={user_id}")
                return Response({
                    "code": 1,
                    "message": "复用未支付订单",
                    "data": {
                        "order_number": reuse_order.order_number,
                        "total_price": float(reuse_order.total_price),
                        "real_price": float(reuse_order.real_price),
                    }
                })
        except Exception as reuse_err:
            logger.warning(f"检查未支付订单复用时发生异常: {reuse_err}")

        # 校验价格
        calculate_total = 0

        for item in course_list:
            course_id = item['course_id']
            expire_time = item['expire_time']
            price = item['price']

            if expire_time == 0:  # 永久有效
                course_price = Course.objects.get(id=course_id).price
                logger.info(f"课程表{course_id}永久有效,价格:{course_price}")

            else:  # 有有限期
                course_price = CourseExpire.objects.get(course_id=course_id, expire_time=expire_time).price
                logger.info(f"课程有效期表{course_id}有限期,价格:{course_price}")
            calculate_total += course_price
            if float(price) != float(course_price):
                logger.error(f"用户{user_id}的购物车课程列表id:{course_ids} 中课程{course_id}价格有误")
                logger.error(f"前端传递的价格:{price},数据库的课程价格:{course_price}")
                return Response({
                    "code": 0,
                    "message": "价格有误",
                })

        if float(total_price) != float(calculate_total):
            logger.error(f"前端传递的总价格:{total_price},数据库计算的总价格:{calculate_total},不一致")
            return Response({
                "code": 0,
                "message": "订单总价格有误",
            })

        # 获取用户级分布式锁，串行化下单创建
        lock_key = f"code_env_spaces:orders:lock:create:{user_id}"
        got_lock = redis_conn.set(lock_key, "1", nx=True, ex=10)
        if not got_lock:
            # 未拿到锁，优先返回幂等结果，否则提示稍后重试
            if idemp_key:
                cached_result = redis_conn.get(idemp_key)
                if cached_result:
                    try:
                        result = json.loads(cached_result.decode())
                        return Response(result)
                    except Exception:
                        pass
            return Response({"code": 0, "message": "订单正在创建中，请稍后重试"}, status=status.HTTP_429_TOO_MANY_REQUESTS)

        try:
            snowflake = SnowflakeSingleton.get_instance(worker_id=2)
            order_number = str(snowflake.get_id())
            logger.info(f"通过雪花算法生成订单编号:{order_number}")
            with transaction.atomic():
                # 创建订单表
                order = Order.objects.create(
                    total_price=total_price,
                    real_price=real_price,
                    order_status=0,
                    pay_method=pay_method,
                    order_number=order_number,
                    use_coupon=bool(use_coupon),
                    use_coupon_id=use_coupon_id,
                    order_desc=f"订单描述:{order_number}",
                    order_time=now(),
                    user=request.user
                )

                logger.info(f"创建订单表成功,订单编号:{order_number},订单数据:{order}")
                # 创建订单详情表
                for item in course_list:
                    course_id = item['course_id']  # 课程ID
                    expire_days = item['expire_time']  # 有效期天数
                    expire_text = item['expire_text']  # 有效期天数文本
                    price = item['price']  # 价格
                    logger.info(
                        f"正在创建订单详情表,课程ID:{course_id},有效期天数:{expire_days},有效期天数文本:{expire_text},价格:{price}")
                    expire_time = None
                    if expire_days != 0:  # 才有有效期
                        expire_time = timezone.now() + timedelta(days=expire_days)

                    order_detail = OrderDetail.objects.create(
                        course=Course.objects.get(id=course_id),
                        order=order,
                        price=price,
                        expire_days=expire_days,
                        expire_text=expire_text,
                        expire_time=expire_time,
                        order_number = order_number
                    )

                    logger.info(f"正在创建的订单详情是:{order_detail}")

            # 删除购物车（失败不影响下单结果）
            try:
                for course_id in course_ids:
                    course_key = f"course_{course_id}"
                    redis_conn.hdel(cart_key, course_key)
            except Exception as del_err:
                logger.warning(f"购物车删除失败: {del_err}")

            response_payload = {
                "code": 1,
                "message": "订单创建成功",
                "data": {
                    "order_number": order_number,
                    "total_price": total_price,
                    "real_price": real_price,
                }
            }

            # 幂等键写入（在事务提交后）
            if idemp_key:
                def _store_idemp():
                    try:
                        redis_conn.set(idemp_key, json.dumps(response_payload, ensure_ascii=False), ex=600)
                    except Exception as e:
                        logger.warning(f"写入幂等结果失败: {e}")

                try:
                    transaction.on_commit(_store_idemp)
                except Exception:
                    _store_idemp()

            # 事务提交后投递延时取消任务（30分钟）
            def _schedule_cancel_task():
                try:
                    cancel_if_unpaid.apply_async(args=[order_number], countdown=30 * 60)
                except Exception as e:
                    logger.error(f"投递自动取消订单任务失败 order_number={order_number}, err={e}")

            try:
                transaction.on_commit(_schedule_cancel_task)
            except Exception:
                _schedule_cancel_task()
            logger.info(f"response_payload:{response_payload}")
            return Response(response_payload)
        except Exception as e:
            logger.error(f"订单创建失败:{str(e)}")
            return Response({
                "code": 0,
                "message": "订单创建失败",
            })
        finally:
            try:
                redis_conn.delete(lock_key)
            except Exception:
                pass


class OrderDetailAPIView(APIView):

    permission_classes = [IsAuthenticated]

    def get(self, request):
        """
        获取订单详情接口
        """
        order_number = request.query_params.get('order_number')
        if not order_number:
            return Response({'code': 0, 'message': '订单号不能为空'})

        try:
            order = Order.objects.get(order_number=order_number, user=request.user)
            ser = OrderDetailInfoSerializer(order)
            return Response({'code': 1, 'message': '获取订单详情成功', 'data': ser.data})

        except Exception as e:
            logger.error(f"获取订单详情异常: {str(e)}")
            return Response({'code': 0, 'message': '获取订单详情失败'})